package com.zcoj.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zcoj.core.model.ApiResponse;
import com.zcoj.core.model.Constants;
import com.zcoj.core.model.PageData;
import com.zcoj.core.model.PageWrap;
import com.zcoj.mapper.JudgeMapper;
import com.zcoj.mapper.ProblemMapper;
import com.zcoj.mapper.SourceMapper;
import com.zcoj.mapper.TagMapper;
import com.zcoj.pojo.Dto.*;
import com.zcoj.pojo.Vo.ProblemCaseVO;
import com.zcoj.pojo.Vo.ProblemPageVO;
import com.zcoj.security.dto.LoginUser;
import com.zcoj.service.*;
import com.zcoj.util.RedisCaches;
import com.zcoj.util.WrapperUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Service实现
 * @author 剩xia的盛夏
 * @date 2023/02/24 16:16
 */
@Service
public class ProblemServiceImpl implements ProblemService {

    @Autowired
    private JudgeMapper judgeMapper;

    @Autowired
    private SourceMapper sourceMapper;

    @Autowired
    private TagMapper tagMapper;
    @Autowired
    private ProblemMapper problemMapper;

    @Autowired
    private RedisCaches redisCaches;

    @Autowired
    private ProblemCaseService problemCaseService;

    @Autowired
    private GroupProblemService groupProblemService;

    @Autowired
    private ContestProblemService contestProblemService;

    @Autowired
    private TrainingProblemService trainingProblemService;




    @Override
    public Integer create(Problem problem) {
        LoginUser loginUser = redisCaches.getLoginUser();
        String username = loginUser.getUsername();
        problem.setAuthor(username);
        problem.setModifiedUser(username);
        problem.setCaseVersion(""+new Date().getTime());
        int insert = problemMapper.insert(problem);
        if(insert!=0){
            //创建对应题目测试案例目录/problem_1000
            FileUtil.mkdir(Constants.File.TESTCASE_BASE_FOLDER.getPath() + "/problem_" + problem.getId());
            return problem.getId();
        }else {
            return 0;
        }
    }

    @Override
    public Boolean isProblemValidID(Integer pid) {
        Problem problem = problemMapper.selectById(pid);
        return Objects.nonNull(problem);
    }


    @Override
    public List<Problem> getProblemList() {
        QueryWrapper<Problem> queryWrapper=new QueryWrapper<>();
        queryWrapper.select("id","tilte");
        return problemMapper.selectList(queryWrapper);
    }

    @Override
    public Integer deleteById(Integer id) {
        ProblemCase problemCase = new ProblemCase();
        problemCase.setPid(id);
        //删除题目先删除题目案例
        List<ProblemCase> problemCaseServiceList = problemCaseService.findList(problemCase);
        for (ProblemCase aCase : problemCaseServiceList) {
            problemCaseService.deleteById(aCase.getId());
        }

        return problemMapper.deleteById(id);
    }

    @Override
    public void deleteByIdInBatch(List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) return;
        problemMapper.deleteBatchIds(ids);
    }

    @Override
    public Integer updateById(Problem problem) {
        LoginUser loginUser = redisCaches.getLoginUser();
        String username = loginUser.getUsername();
        problem.setModifiedUser(username);
        return problemMapper.updateById(problem);
    }

    @Override
    public void updateByIdInBatch(List<Problem> problems) {
        if (CollectionUtils.isEmpty(problems)) return;
        for (Problem problem: problems) {
            this.updateById(problem);
        }
    }

    @Override
    public Problem findById(Integer id) {
        return problemMapper.selectById(id);
    }

    @Override
    public Problem findOne(Problem problem) {
        Wrapper<Problem> wrapper = new QueryWrapper<>(problem);
        return problemMapper.selectOne(wrapper);
    }

    @Override
    public List<Problem> findList(Problem problem) {
        Wrapper<Problem> wrapper = new QueryWrapper<>(problem);
        return problemMapper.selectList(wrapper);
    }
  
    @Override
    public PageData<Problem> findPage(PageWrap<Problem> pageWrap) {
        IPage<Problem> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        Problem problem = WrapperUtil.blankToNull(pageWrap.getModel());
        QueryWrapper<Problem> queryWrapper = new QueryWrapper<>();
        for(PageWrap.SortData sortData: pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase("DESC")) {
                queryWrapper.orderByDesc(sortData.getProperty());
            } else {
                queryWrapper.orderByAsc(sortData.getProperty());
            }
        }
        if(Objects.nonNull(problem.getTilte())){
            queryWrapper.like("tilte",problem.getTilte());
        }
        if(Objects.nonNull(problem.getStatus())){
            queryWrapper.eq("status",problem.getStatus());
        }
        queryWrapper.select("id","tilte","author","gid","tid","cid","type","status","oi_score","create_time","update_time","modified_user");
        return PageData.from(problemMapper.selectPage(page, queryWrapper));
    }

    @Override
    public PageData<ProblemPageVO> findPageVO(PageWrap<Problem> pageWrap) {
        LoginUser loginUser = redisCaches.getLoginUser();
        IPage<Problem> page=new Page<>(pageWrap.getPage(),pageWrap.getCapacity());

        QueryWrapper<Problem> queryWrapper=new QueryWrapper<>();
        Problem problem = WrapperUtil.blankToNull(pageWrap.getModel());
        if(Objects.nonNull(problem.getTilte())){
            queryWrapper.like("tilte",problem.getTilte());
        }
        if(Objects.nonNull(problem.getTag())&&problem.getTag()!=0){
            queryWrapper.eq("tag",problem.getTag());
        }
        if(Objects.nonNull(problem.getSource())&&problem.getSource()!=0){
            queryWrapper.eq("source",problem.getSource());
        }
        if(Objects.nonNull(problem.getGid())&&problem.getGid()!=0){
            queryWrapper.eq("gid",problem.getGid());
        }

        if(Objects.nonNull(problem.getTid())&&problem.getTid()!=0){
            queryWrapper.eq("tid",problem.getTid());
        }
        if(Objects.nonNull(problem.getCid())&&problem.getCid()!=0){
            queryWrapper.eq("cid",problem.getCid());
        }else{
            if(Objects.nonNull(problem.getStatus())){
                queryWrapper.eq("status",problem.getStatus());
            }
        }

        queryWrapper.select("id","tilte","source","tag");
        IPage<Problem> iPage = problemMapper.selectPage(page, queryWrapper);
        List<ProblemPageVO> problemPageVOS=new ArrayList<>();


        //查询SubmitNumber AccessNumber
        for (Problem p : iPage.getRecords()) {
            ProblemPageVO problemPageVO = new ProblemPageVO();
            problemPageVO.setId(p.getId());
            problemPageVO.setTilte(p.getTilte());
            problemPageVO.setSource(p.getSource()==null?0:p.getSource());
            problemPageVO.setTag(p.getTag()==null?0:p.getTag());
            QueryWrapper<Judge> judgeQueryWrapper=new QueryWrapper<>();
            judgeQueryWrapper.eq("pid",p.getId());
            problemPageVO.setSubmitNumber(judgeMapper.selectCount(judgeQueryWrapper));
            judgeQueryWrapper.eq("status",0);
            problemPageVO.setAccessNumber(judgeMapper.selectCount(judgeQueryWrapper));
            problemPageVOS.add(problemPageVO);
        }


        //将Probelem类映射到Vo类上
        IPage<ProblemPageVO> problemPageVOIPage=new Page<>();
        problemPageVOIPage.setTotal(iPage.getTotal());
        problemPageVOIPage.setCurrent(iPage.getCurrent());
        problemPageVOIPage.setSize(iPage.getSize());
        problemPageVOIPage.setRecords(problemPageVOS);
        return PageData.from(problemPageVOIPage);
    }

    /**
     * @method: [findCompentPageVO]
     * @param: [pageWrap]
     * @returns: com.zcoj.core.model.PageData<com.zcoj.pojo.Vo.ProblemPageVO>
     * @description:   前台组件无条件查询
     * @author: chen.bin
     * @version: 1.0.0
     * @date 2023/4/21 22:19
     **/
    @Override
    public PageData<ProblemPageVO> findComponentPageVO(PageWrap<Problem> pageWrap) {
        IPage<Problem> page=new Page<>(pageWrap.getPage(),pageWrap.getCapacity());

        QueryWrapper<Problem> queryWrapper=new QueryWrapper<>();
        Problem problem = WrapperUtil.blankToNull(pageWrap.getModel());


        if(Objects.nonNull(problem.getGid())&&problem.getGid()!=0){
            //queryWrapper.eq("gid",problem.getGid());
            GroupProblem groupProblem = new GroupProblem();
            groupProblem.setGid(problem.getGid());
            List<GroupProblem> list = groupProblemService.findList(groupProblem);

            if(list.size()!=0){
                queryWrapper.nested(i -> {
                    for (GroupProblem gp : list) {
                        i.or(j -> j.eq("id", gp.getPid()));
                    }
                });
            }else {
                queryWrapper.eq("id",0);
            }


        }else if(Objects.nonNull(problem.getTid())&&problem.getTid()!=0){
            //queryWrapper.eq("tid",problem.getTid());
            TrainingProblem trainingProblem=new TrainingProblem();
            trainingProblem.setTid(problem.getTid());
            List<TrainingProblem> trainingProblemList = trainingProblemService.findList(trainingProblem);

            if(trainingProblemList.size()!=0){
                queryWrapper.nested(i->{
                    for (TrainingProblem tp : trainingProblemList) {
                        i.or(j->j.eq("id",tp.getPid()));
                    }
                });
            }else {
                queryWrapper.eq("id",0);
            }

        }else if(Objects.nonNull(problem.getCid())&&problem.getCid()!=0){
            //queryWrapper.eq("cid",problem.getCid());
            ContestProblem contestProblem = new ContestProblem();
            contestProblem.setCid(problem.getCid());
            List<ContestProblem> list = contestProblemService.findList(contestProblem);
            if(list.size()!=0){
                queryWrapper.nested(i->{
                    for (ContestProblem cp : list) {
                        i.or(j->j.eq("id",cp.getPid()));
                    }
                });
            }else {
                queryWrapper.eq("id",0);
            }

        }
        queryWrapper.select("id","tilte","source","tag");
        IPage<Problem> iPage = problemMapper.selectPage(page, queryWrapper);
        List<ProblemPageVO> problemPageVOS=new ArrayList<>();


        //查询SubmitNumber AccessNumber
        for (Problem p : iPage.getRecords()) {
            ProblemPageVO problemPageVO = new ProblemPageVO();
            problemPageVO.setId(p.getId());
            problemPageVO.setTilte(p.getTilte());
            problemPageVO.setSource(p.getSource()==null?0:p.getSource());
            problemPageVO.setTag(p.getTag()==null?0:p.getTag());
            QueryWrapper<Judge> judgeQueryWrapper=new QueryWrapper<>();
            judgeQueryWrapper.eq("pid",p.getId());
            problemPageVO.setSubmitNumber(judgeMapper.selectCount(judgeQueryWrapper));
            judgeQueryWrapper.eq("status",0);
            problemPageVO.setAccessNumber(judgeMapper.selectCount(judgeQueryWrapper));
            problemPageVOS.add(problemPageVO);
        }


        //将Probelem类映射到Vo类上
        IPage<ProblemPageVO> problemPageVOIPage=new Page<>();
        problemPageVOIPage.setTotal(iPage.getTotal());
        problemPageVOIPage.setCurrent(iPage.getCurrent());
        problemPageVOIPage.setSize(iPage.getSize());
        problemPageVOIPage.setRecords(problemPageVOS);
        return PageData.from(problemPageVOIPage);
    }

    @Override
    public long count(Problem problem) {
        Wrapper<Problem> wrapper = new QueryWrapper<>(problem);
        return problemMapper.selectCount(wrapper);
    }

    @Override
    public ApiResponse getProblemSource() {
        String problemSourceRedisKey="problemSourceRedisKey";
        Object redisCacheCacheObject = redisCaches.getCacheObject(problemSourceRedisKey);
        if( Objects.isNull(redisCacheCacheObject)){
            List<Source> sources = sourceMapper.selectList(new QueryWrapper<Source>());
            redisCaches.setCacheObject(problemSourceRedisKey,sources,1, TimeUnit.DAYS);
            return ApiResponse.success("",JSON.toJSON(sources));
        }else {
           return ApiResponse.success("",JSON.toJSON(redisCacheCacheObject));
        }

    }

    @Override
    public ApiResponse getProblemTag() {
        String tagSourceRedisKey="tagSourceRedisKey";
        Object redisCacheCacheObject = redisCaches.getCacheObject(tagSourceRedisKey);
        if( Objects.isNull(redisCacheCacheObject)){
            List<Tag> tags = tagMapper.selectList(new QueryWrapper<Tag>());
            redisCaches.setCacheObject(tagSourceRedisKey,tags,1, TimeUnit.DAYS);
            return ApiResponse.success("",JSON.toJSON(tags));
        }else {
            return ApiResponse.success("",JSON.toJSON(redisCacheCacheObject));
        }
    }

    @Override
    public Integer newProblemTestcaseFile(String fileName, Integer pid){
        String inputPath=Constants.File.TESTCASE_BASE_FOLDER.getPath()+"/problem_"+pid+"/"+fileName+".in";
        String outputPath=Constants.File.TESTCASE_BASE_FOLDER.getPath()+"/problem_"+pid+"/"+fileName+".out";
        File mkdir = FileUtil.touch(inputPath);
        File mkdir1 = FileUtil.touch(outputPath);
        if(Objects.nonNull(mkdir)&&Objects.nonNull(mkdir1)){
            ProblemCase problemCase1 = new ProblemCase();
            problemCase1.setPid(pid);
            //测试案例数量
            int count = Math.toIntExact(problemCaseService.count(problemCase1));
            ProblemCase problemCase = new ProblemCase();
            problemCase.setPid(pid);
            problemCase.setInput(fileName+".in");
            problemCase.setOutput(fileName+".out");
            Integer integer = problemCaseService.create(problemCase);
            if(integer!=0){
                Problem byId = findById(pid);
                Integer oiScore = byId.getOiScore();
                Integer score= oiScore / (count + 1);
                List<ProblemCase> problemCaseList = problemCaseService.findList(null);
                for(int i = 0; i < problemCaseList.size()-1; i++) {
                    ProblemCase problemCase2 = problemCaseList.get(i);
                    problemCase2.setScore(score);
                    problemCaseService.updateById(problemCase2);
                }

                //最后一位problemCase分数可能除不尽
                ProblemCase problemCase3 = problemCaseList.get(problemCaseList.size() - 1);
                if(score*(count+1)!=oiScore){
                    problemCase3.setScore(oiScore-score*count);
                }else {
                    problemCase3.setScore(score);
                }
                problemCaseService.updateById(problemCase3);
                String uuidVersion= UUID.randomUUID().toString().substring(0,5);
                Problem problem = new Problem();
                problem.setId(pid);
                problem.setCaseVersion(uuidVersion);
                updateById(problem);
                return 1;//创建成功
            }else {
                return 0;//数据写入失败
            }

        }else {
            return -1;//创建文件失败
        }
    }

    @Override
    public void writeProblemCase(ProblemCaseVO problemCaseVO) {
        String suffix = FileNameUtil.extName(problemCaseVO.getFileName());
        if( Objects.nonNull(suffix)&&!(suffix.equals("in")||suffix.equals("out"))){
            throw new RuntimeException("题目测试案例文件后缀名不合法");
        }
        String fileName= Constants.File.TESTCASE_BASE_FOLDER.getPath()+"/problem_"+problemCaseVO.getPid()+"/"+problemCaseVO.getFileName();
        if(!FileUtil.exist(new File(fileName))){
            throw new RuntimeException("文件不存在");
        }
        FileWriter fileWriter = new FileWriter(fileName);
        fileWriter.write(problemCaseVO.getContent());
        String uuidVersion= UUID.randomUUID().toString().substring(0,5);
        Problem problem = new Problem();
        problem.setId(problemCaseVO.getPid());
        problem.setCaseVersion(uuidVersion);
        updateById(problem);
    }
}
